package com.mach.platform.service.js.iot;

import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import com.mach.platform.constants.CommonConstants;
import com.mach.platform.domain.Response;
import com.mach.platform.service.IotDevServiceImpl;
import com.mach.platform.service.UServ;
import com.mach.platform.service.js.JsService;
import com.mach.platform.util.PairModel;
import com.mach.platform.util.SqlHelper;
import com.mach.platform.utils.CacheUtils;
import com.mach.platform.utils.SpringUtil;
import com.mach.platform.utils.StrUtil;
import com.mach.platform.utils.springdata.Collections3;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.text.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * IOTJsService 是js引擎，用来执行设备脚本
 */
@Slf4j
@Service
public class IOTJsService extends HashMap<String, Object> implements Runnable {

    private ScriptEngine js;
    private SqlHelper sqlHelper;
    //private SqlHelperJS sqlHelperJS;
    @Autowired
    private DataSource dataSource;
    private boolean isRun = false;
    private Map<Object, Map<String, Object>> devsInfo;

    @Autowired
    private IotDevServiceImpl iotDevService;

    public IOTJsService() {

    }

    @PostConstruct
    public void startVirService() {
        this.init();
    }

    /**
     * 初始化服务方法
     */
    private void init() {
        // 加入数据源
        this.sqlHelper = SqlHelper.getBySource(this.dataSource);
        this.startUp();
    }

    public void startUp() {
//        this.js = JsService.get().newNashornEng();

        //this.sqlHelperJS =  SqlHelperJS.getBySource(this.dataSource, this.js);

        //this.runPreJs();
        //加载设备数据
        Thread thread = new Thread(this);
        thread.start();

        //启动pick
        this.isRun = true;

        //U.cachedThreadPool.execute(this);
    }

    public SqlHelperJS db() {
        SqlHelperJS sqlHelperjs = SqlHelperJS.getBySource(this.dataSource, this.newNgHasRunPreJs());
        return sqlHelperjs;
    }

    public SqlHelper sqlHelper() {
        return this.sqlHelper;
    }

    /**
     * 调用bean上下文操作
     * js 调用： var methodResult  = iot.ctx(类名|默认).xxMethod(params.....)
     *
     * @param beanName
     * @return
     */
    public Object ctx(String beanName) {
        return SpringUtil.getBean(beanName);
    }

    private void runPreJs() {
        String preJs = "";
//        ClassUtils.getDefaultClassLoader().getResourceAsStream("/com/mach/platform/service/js/iot/");

        this.js.put("Ctx", new Ctx());
        try {
            preJs = StrUtil.getCtxByStm(this.getClass().getResourceAsStream("/pre.js"), 1024, "utf-8");
            if (preJs != null && !"".equals(preJs)) {
                this.runJS(preJs);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private ScriptEngine newNgHasRunPreJs() {
        String preJs = "";
//        ClassUtils.getDefaultClassLoader().getResourceAsStream("/com/mach/platform/service/js/iot/");

        ScriptEngine ng = JsService.get().newNashornEng();
        ng.put("Ctx", new Ctx());
        try {
            preJs = StrUtil.getCtxByStm(this.getClass().getResourceAsStream("/pre.js"), 1024, "utf-8");
            if (preJs != null && !"".equals(preJs)) {
//                this.runJS(preJs);

                ng.eval(preJs);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ng;
    }


    /**
     * 刷新IOTJs平台
     */
    public void flushAndReload() {
        shutDown();
        startUp();
    }

    public void shutDown() {
        this.clear();
        this.js = null;
    }

    private void setTheDevs(Map<Object, Map<String, Object>> dbObj) {

    }


    private void pick() {

    }


    @Override
    public void run() {
        loadDevInfo();

        while (this.isRun) {
//            this.pick();
            try {
                Thread.sleep(1 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.isRun = false;
    }


    /**
     * 加载设备数据
     */
    private void loadDevInfo() {
        //从数据库中加载设备数据
        String sql2GetDevs = "select dev.id devId, dev.name name,dev.dev_type_id devTypeId,\n" +
                " dev.comm_addr commAddr,\n" +
                " type.prol prol,  \n" +
                " type.script_switch isRunTypeJS,type.script typeJS,  \n" +
                " dev.script_switch isRunDevJS,dev.script devJS,  \n" +
                " coll.id collId, coll.name collName," +
                " coll.value_addr collAddr, coll.value_addr_str collAddrStr,\n " +
                " coll.data_len collLen, coll.data_type collDataType,coll.unit collUnit,\n" +
                " '' from iot_dev dev\n" +
                " LEFT JOIN iot_coll_items coll on dev.dev_type_id=coll.dev_type_id\n" +
                " LEFT JOIN iot_dev_type type on type.id=dev.dev_type_id \n" +
                " where 1=1 \n" +
//                " and coll.is_get = 1 \n" +
//                " and dev.id = 221 \n" +
                " order by devId,collId ";

        Map<Object, Map<String, Object>> dbObj;
        try {
            dbObj = sqlHelper.queryPairModel(sql2GetDevs, new PairModel("dev", "devId", "coll", true));
            this.devsInfo = dbObj;
            Set<Entry<Object, Map<String, Object>>> allDevEntrys = dbObj.entrySet();
            for (Entry<Object, Map<String, Object>> enery : allDevEntrys) {
                Map<String, Object> devInfo = enery.getValue();
                GeneralModelDev dev = ModelDevFactory.getOrNew(devInfo, this);
                if (dev != null && !dev.hasStart()) {
                    dev.start();
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public Object runJS(String script) {
        try {
            return this.js.eval(script);
        } catch (ScriptException e) {
            e.printStackTrace();
        }
        return null;
    }

    private Random random = new Random();

    public Double rand() {
        return random.nextDouble();
    }

    public List<Object> list() {
        return new ArrayList<Object>();
    }

    public void breakPoint(Map<Object,Object> obj){
        System.out.print("breakPoing obj=" + obj.toString());
    }

    /**
     * 保留数字小数点位数
     * 用法：getNumberFormat("0.00").format(|num|)
     *
     * @param pattern
     * @return
     */
    public NumberFormat getNumberFormat(String pattern) {
        return new DecimalFormat(pattern);
    }


    /**
     * 根据时间获取时间参数为：年, 月,日
     *
     * @param time
     * @return
     */
    public String getTimeByFormat(String time) {
        return UServ.getTimeByFormat(time);
    }

    public DateFormat getDateFormat(String partten) {
        return new SimpleDateFormat(partten);
    }

    /**
     * 临时方法 // todo delete
     *
     * @param keyName 换成Key
     * @param saveObj 缓存对象
     * @return
     */
    public Object makeAndGetCache(String keyName, Object saveObj) {
        if (saveObj == null) {
            // 获取缓存
            return CacheUtils.guavaCacheFactory.getIfPresent(keyName);
        }
        // 缓存数据
        CacheUtils.guavaCacheFactory.put(keyName, saveObj);
        return null;
    }


    public String getPercentage(Double number) {
        DecimalFormat df = new DecimalFormat("0.0%");
        return df.format(number);
    }


    /**
     * 新版同类对比接口，因为逻辑比较复杂，暂时先把js代码写到后台
     * @param name 能源对应的英文名称
     * @param workSpaceName 空间名称
     * @param time 时间
     * @param selectType total：用量数据 single：单车数据
     * @return
     */
    public List<Map> newSimilarContrast(String name, String workSpaceName, String time, String selectType) {
        String type = reflectEnergyName(name);
        return iotDevService.similarContrast(type, workSpaceName, time, selectType);
    }


    /**
     * /**
     * 通过多个架构名称查询对应电和燃气的架构id(自定义对比专用)
     * @param workSpaceNames 多个架构名称，以逗号分隔
     * @return
     */
    public String getEnergyStrucIdsByName(String workSpaceNames) {
        String sql = "select id from energy_struc where ";
        String[] energyNameArr = workSpaceNames.split(",");
        for (int i = 0; i < energyNameArr.length; i++) {
            String[] names = energyNameArr[i].split("/");
            if (i > 0) {
                sql += " or ";
            }
            if (names.length == 1) {
                sql += " ((" + "name = '" + names[0] + "') and (type = '电' or type = '燃气'))";
            }
            if (names.length >= 2) {
                sql += " ((" + "name = '" + names[names.length - 1] + "' and factory = '" +  CommonConstants.factoryName.get(names[1]) + "') and (type = '电' or type = '燃气'))";
            }
        }
        List<Map<String,Object>> result = null;
        try {
            result = this.db().queryList(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return Collections3.extractToString(result, "id", ",");
    }


    /**
     * 通过多个架构名称和能源类型查询对应的多个架构id字符串拼接(自定义对比专用)
     * @param workSpaceNames 多个架构名称，以逗号分隔
     * @param type 能源类型
     * @return
     */
    public String getEnergyStrucIdsByNameType(String workSpaceNames, String type) {
        String sql = "select id from energy_struc where ";
        String[] energyNameArr = workSpaceNames.split(",");
        for (int i = 0; i < energyNameArr.length; i++) {
            String[] names = energyNameArr[i].split("/");
            if (i > 0) {
                sql += " or ";
            }
            if (names.length == 1) {
                sql += " (" + "name = '" + names[0] + "' and type = '"+ type +"')";
            }
            if (names.length >= 2) {
                sql += " (" + "name = '" + names[names.length - 1] + "' and factory = '" + CommonConstants.factoryName.get(names[1]) + "' and type = '" + type + "')";
            }
        }
        List<Map<String,Object>> result = null;
        try {
            result = this.db().queryList(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return Collections3.extractToString(result, "id", ",");
    }


    /**
     * 通过架构名称和能源类型获取到架构id(EMS专用)
     * @param workSpaceName 空间名称
     * @param type 能源类型
     * @return
     */
    public Object getEnergySpaceId(String workSpaceName, String type) {
        type = reflectEnergyName(type);
        List<String> strings = Splitter.on("/").splitToList(workSpaceName);
        String sql = "";
        if (strings.size() == 1) {
            sql = "select id from energy_struc where name = '" + strings.get(0) + "' and type = '" + type + "'";
        }
        if (strings.size() == 2) {
            sql = "select id from energy_struc where name = '" + strings.get(1) + "' and type = '" + type + "'";
        }
        if (strings.size() == 3) {
            sql = "select one.id\n" +
                    "from energy_struc one\n" +
                    "         left join energy_struc two on two.id = one.pid\n" +
                    "where one.type = '" + type + "'\n" +
                    " and two.name = '" + strings.get(1) + "' and one.name = '" + strings.get(2) + "'";
        }
        Map<String, Object> queryResult = null;
        try {
            queryResult = this.db().queryOne(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        if (queryResult != null) {
            return queryResult.get("id") == null ? -1 : queryResult.get("id");
        } else {
            return -1;
        }

    }

    public Object bridgeOtherJsResult(String params) {
        HashMap param = JSON.parseObject(params, HashMap.class);

        if (param.get("id") == null) {
            return -1;
        }

        Response response = Response.builder().build();
        GeneralModelDev modelDev = ModelDevFactory.getById(Integer.parseInt(param.get("id").toString()));
        if (null != modelDev && param.size() > 1) {
            try {
                modelDev.sendAction(response, param);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return response;
    }

    public String reflectEnergyName(String name) {
        if (CommonConstants.EMNUS.get(name) == null) {
            return "";
        }
        return CommonConstants.EMNUS.get(name);
    }

    /**
     * 获取上一年的时间
     * @param date 当前时间
     * @param timeFormat 格式
     * @return 上一年时间
     */
    public String lastTime(String date, String timeFormat){
        if (!StringUtils.isNoneBlank(date, timeFormat)){
            return null;
        }
        DateFormat df = new SimpleDateFormat(timeFormat);
        try {
            Date d1 = df.parse(date);
            Calendar  g = Calendar.getInstance();
            g.setTime(d1);
            g.add(Calendar.YEAR,-1);
            Date d2 = g.getTime();
            return df.format(d2);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 反转List数组
     * @param list
     * @return
     */
    public List reverse(List list){
        Collections.reverse(list);
        return list;
    }

    public String handleEnergyFramework(){
        Map result = null;
        List<Map<String,Object>> mapList = null;
        try {
            mapList = this.db().queryList("SELECT es.id, es.type, es.name, es.factory, esp.sn FROM `energy_struc` es\n" +
                    "LEFT JOIN `energy_struc_prop` esp on es.id = esp.energy_struc_id\n" +
                    "where 1=1\n" +
                    "-- and es.name = '树脂车间'\n" +
                    "-- and es.factory = 'ZZ'\n" +
                    "and (es.type = '碳排' or es.type = '成本')\n" +
                    "and esp.field_type = '用量'\n" +
                    "and esp.type = 2\n" +
                    "and es.`level` < 3\n" +
                    "and es.name != 'PV'\n" +
                    "union all\n" +
                    "SELECT es.id, es.type, es.name, es.factory, esp.sn FROM `energy_struc` es\n" +
                    "LEFT JOIN `energy_struc_prop` esp on es.id = esp.energy_struc_id\n" +
                    "where 1=1\n" +
                    "-- and es.name = '树脂车间'\n" +
                    "-- and es.factory = 'ZZ'\n" +
                    "and (es.type = '电' or es.type = '燃气')\n" +
                    "and esp.field_type = '用量'\n" +
                    "and esp.type = 1\n" +
                    "and es.`level` < 3\n" +
                    "and es.name != 'PV'");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        if (CollectionUtils.isNotEmpty(mapList)) {
            //分开工厂
            Map<Object, List<Map<String, Object>>> groupingFactory = mapList.stream().collect(Collectors.groupingBy((Map map) -> map.get("factory")));
            //分开车间
            result = handleWorkshop(groupingFactory);
//            String string = JSONObject.toJSONString(result);
            String string = JSONUtils.toJSONString(result);
            return string;
        }
        return null;
    }

    private Map handleWorkshop(Map<Object, List<Map<String, Object>>> groupingFactory) {
        Map result = Maps.newHashMap();//工厂map
        for (Entry entryFactory : groupingFactory.entrySet()){
            List<Map<String, Object>> workshopList = (List<Map<String, Object>>) entryFactory.getValue();
            Map<Object, List<Map<String, Object>>> groupingName = workshopList.stream().collect(Collectors.groupingBy((Map map) -> map.get("name")));

            Object factoryName = entryFactory.getKey();//工厂名称
            Map carbonRowAndCostMap = Maps.newHashMap();//碳排成本map
            for (Entry entryName : groupingName.entrySet()){
                List<Map<String, Object>> mapList = (List<Map<String, Object>>) entryName.getValue();
                Map item = Maps.newHashMap();//能源map
                for (Map map : mapList){

                    if (Objects.equals("碳排", map.get("type").toString())){
                        carbonRowAndCostMap.put(map.get("id").toString(), item);
                    }
                    if (Objects.equals("成本", map.get("type").toString())){
                        carbonRowAndCostMap.put(map.get("id").toString(), item);
                    }
                    if (Objects.equals("电", map.get("type").toString())){
                        item.put("ele", map.get("sn"));
                    }
                    if (Objects.equals("燃气", map.get("type").toString())){
                        item.put("gas", map.get("sn"));
                    }
                }

            }
            result.put(factoryName, carbonRowAndCostMap);
        }
        return result;
    }

    public String getTimeByTime(String time){
        if (StringUtils.isEmpty(time)){
            return null;
        }
        try {
            DateTime dateTime = DateTime.parse(time);
            if (time.length() == 4) {
                return dateTime.plusYears(-1).toString("yyyy");
            } else if (time.length() == 7) {
                return dateTime.plusMonths(-1).toString("yyyy-MM");
            } else if (time.length() == 10){
                return dateTime.plusDays(-1).toString("yyyy-MM-dd");
            }
        } catch (Exception e) {
        }
        return "不支持小时";
    }

}
